Passed
Push — master ( c6b313...ea8146 )
by Kyungmi
01:36
created

common-util.js ➔ ... ➔ ???   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 7
Bugs 0 Features 0
Metric Value
cc 1
c 7
b 0
f 0
nc 1
dl 0
loc 1
rs 10
nop 1
1
/**
2
 * Util functions
3
 *
4
 * @since 1.0.0
5
 */
6
7
/**
8
 * Get client IP address
9
 *
10
 * Will return 127.0.0.1 when testing locally
11
 * Useful when you need the user ip for geolocation or serving localized content
12
 *
13
 * @param request
14
 * @returns {string} ip
15
 */
16
exports.getClientIp = (request) => {
17
  // the ipAddress we return
18
  let ipAddress;
19
20
  // workaround to get real client IP
21
  // most likely because our app will be behind a [reverse] proxy or load balancer
22
  const clientIp = request.headers['x-client-ip'];
23
  const forwardedForAlt = request.headers['x-forwarded-for'];
24
  const realIp = request.headers['x-real-ip'];
25
26
  // more obsure ones below
27
  const clusterClientIp = request.headers['x-cluster-client-ip'];
28
  const forwardedAlt = request.headers['x-forwarded'];
29
  const forwardedFor = request.headers['forwarded-for'];
30
  const forwarded = request.headers['forwarded'];
31
32
  // remote address check
33
  const reqConnectionRemoteAddress = request.connection ? request.connection.remoteAddress : null;
34
  const reqSocketRemoteAddress = request.socket ? request.socket.remoteAddress : null;
35
  const reqConnectionSocketRemoteAddress = (request.connection && request.connection.socket)
36
    ? request.connection.socket.remoteAddress : null;
37
  const reqInfoRemoteAddress = request.info ? request.info.remoteAddress : null;
38
39
  if (clientIp) {
40
    // x-client-ip
41
    ipAddress = clientIp;
42
  } else if (forwardedForAlt) {
43
    // x-forwarded-for
44
    // (typically when your node app is behind a load-balancer (eg. AWS ELB) or proxy)
45
    //
46
    // x-forwarded-for may return multiple IP addresses in the format:
47
    // "client IP, proxy 1 IP, proxy 2 IP"
48
    // Therefore, the right-most IP address is the IP address of the most recent proxy
49
    // and the left-most IP address is the IP address of the originating client.
50
    // source: http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/x-forwarded-headers.html
51
    const forwardedIps = forwardedForAlt.split(',');
52
    ipAddress = forwardedIps[0];
53
  } else if (realIp) {
54
    // x-real-ip
55
    // (default nginx proxy/fcgi)
56
    // alternative to x-forwarded-for, used by some proxies
57
    ipAddress = realIp;
58
  } else if (clusterClientIp) {
59
    // x-cluster-client-ip
60
    // (Rackspace LB and Riverbed's Stingray)
61
    // http://www.rackspace.com/knowledge_center/article/controlling-access-to-linux-cloud-sites-based-on-the-client-ip-address
62
    // https://splash.riverbed.com/docs/DOC-1926
63
    ipAddress = clusterClientIp;
64
  } else if (forwardedAlt) {
65
    // x-forwarded
66
    ipAddress = forwardedAlt;
67
  } else if (forwardedFor) {
68
    // forwarded-for
69
    ipAddress = forwardedFor;
70
  } else if (forwarded) {
71
    // forwarded
72
    ipAddress = forwarded;
73
  } else if (reqConnectionRemoteAddress) {
74
    // remote address checks
75
    ipAddress = reqConnectionRemoteAddress;
76
  } else if (reqSocketRemoteAddress) {
77
    ipAddress = reqSocketRemoteAddress;
78
  } else if (reqConnectionSocketRemoteAddress) {
79
    ipAddress = reqConnectionSocketRemoteAddress;
80
  } else if (reqInfoRemoteAddress) {
81
    ipAddress = reqInfoRemoteAddress;
82
  } else {
83
    // return null if we cannot find an address
84
    ipAddress = null;
85
  }
86
87
  return ipAddress;
88
};
89
90
/**
91
 * Parse below "limited" formatted Semantic Version to an array for UI expression
92
 *  - equals: "=2.3", "=2", "=4.3.3"
93
 *  - range: ">=1.2.3 <3.0", ">=1.2.3", "<3.0.0"  (only permitted gte(>=) and lt(<) for ranges)
94
 *  - all: "*"
95
 *  - mixed (by OR): ">=1.2.3 <3.0.0 || <0.1.2 || >=5.1"
96
 * @param {string} conditionString
97
 * @return {Array}
98
 */
99
const regex = /([>=<]{1,2})([0-9a-zA-Z\-.]+)*/g;
100
exports.parseSemVersion = (semVerString) => {
101
  if (!semVerString) {
102
    return [{ comparator: '*' }];   // default
103
  }
104
  const conditions = semVerString.split('||').map(cond => cond.trim());  // OR 연산을 기준으로 분리
105
  const result = [];
106
  conditions.forEach((cond) => {
107
    regex.lastIndex = 0;  // reset find index
108
    if (cond.startsWith('*')) {
109
      result.push({ comparator: '*' });
110
    } else if (cond.startsWith('>=') || cond.startsWith('<')) {
111
      const resultItem = { comparator: '~' };
112
      let execResult;
113
      while ((execResult = regex.exec(cond)) !== null) {
114
        if (execResult[1] === '>=') {
115
          resultItem.versionStart = execResult[2];
116
        } else if (execResult[1] === '<') {
117
          resultItem.versionEnd = execResult[2];
118
        }
119
      }
120
      if (resultItem.versionStart || resultItem.versionEnd) {
121
        result.push(resultItem);
122
      }
123
    } else if (cond.startsWith('=')) {
124
      let execResult;
125
      if ((execResult = regex.exec(cond)) !== null) {
126
        result.push({ comparator: '=', version: execResult[2] });
127
      }
128
    }
129
  });
130
  return result;
131
};
132
133
/**
134
 * Stringify parsed version conditions to SemVer representation
135
 * @param {Array} parsedConditions
136
 * @return {string}
137
 */
138
exports.stringifySemVersion = (parsedConditions) => {
139
  const result = parsedConditions.map((cond) => {
140
    switch (cond.comparator) {
141
      case '*':
142
        return '*';
143
      case '=':
144
        return `=${cond.version}`;
145
      case '~':
146
        return `${cond.versionStart ? `>=${cond.versionStart}` : ''} ${cond.versionEnd ? `<${cond.versionEnd}` : ''}`;
147
      default:
148
        return '';
149
    }
150
  });
151
  if (result.includes('*')) {
152
    return '*';
153
  }
154
  return result.filter(cond => !!cond).join(' || ').trim();
155
};
156
157
/**
158
 * Replace camelCase string to snake_case
159
 * @param {string} str
160
 * @returns {string}
161
 */
162
exports.camel2snake = (str) => {
163
  if (typeof str === 'string') {
164
    // ignore first occurrence of underscore(_) and capital
165
    return str.replace(/^_/, '').replace(/^([A-Z])/, $1 => `${$1.toLowerCase()}`).replace(/([A-Z])/g, $1 => `_${$1.toLowerCase()}`);
166
  }
167
  return str;
168
};
169
170
/**
171
 * Replace camelCase string to snake_case on the property names in objects
172
 * @param {Array|Object} object
173
 * @returns {*}
174
 */
175
exports.camel2snakeObject = (object) => {
176
  if (object instanceof Array) {
177
    const result = [];
178
    for (let i = 0, n = object.length; i < n; i++) {
179
      result[i] = exports.camel2snakeObject(object[i]);
180
    }
181
    return result;
182
  } else if (object && typeof object === 'object') {
183
    const result = {};
184
    for (let prop in object) {
185
      if (Object.prototype.hasOwnProperty.call(object, prop)) {
186
        result[exports.camel2snake(prop)] = exports.camel2snakeObject(object[prop]);
187
      }
188
    }
189
    return result;
190
  }
191
  return object;
192
};
193
194
/**
195
 * Replace snake_case string to camelCase
196
 * @param {string} str
197
 * @returns {string}
198
 */
199
exports.snake2camel = (str) => {
200
  if (typeof str === 'string') {
201
    // ignore first occurrence of underscore(_)
202
    return str.replace(/^_/, '').replace(/_([a-z0-9]?)/g, ($1, $2) => `${$2.toUpperCase()}`);
203
  }
204
  return str;
205
};
206
207
/**
208
 * Replace snake_case string to camelCase on the property names in objects
209
 * @param {Array|Object} object
210
 * @returns {*}
211
 */
212
exports.snake2camelObject = (object) => {
213
  if (object instanceof Array) {
214
    const result = [];
215
    for (let i = 0, n = object.length; i < n; i++) {
216
      result[i] = exports.snake2camelObject(object[i]);
217
    }
218
    return result;
219
  } else if (object && typeof object === 'object') {
220
    let result = {};
221
    for (let prop in object) {
222
      if (Object.prototype.hasOwnProperty.call(object, prop)) {
223
        result[exports.snake2camel(prop)] = exports.snake2camelObject(object[prop]);
224
      }
225
    }
226
    return result;
227
  }
228
  return object;
229
};
230